﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using ProjectEuler.Linq;

namespace ProjectEuler.Problems
{
    [EulerProblem(37, 748317)]
    [Description("Find the sum of all eleven primes that are both truncatable from left to right and right to left.")]
    internal sealed class Problem037 : EulerProblem
    {
        public override Object Solve()
        {
            return TwoSidedPrimes().Take(11).Sum();
        }

        private static IEnumerable<Int32> TwoSidedPrimes()
        {
            var list = new List<Int32>();

            TwoSidedPrimes(list, 2, 1);
            TwoSidedPrimes(list, 3, 1);
            TwoSidedPrimes(list, 5, 1);
            TwoSidedPrimes(list, 7, 1);

            return list;
        }

        private static void TwoSidedPrimes(List<Int32> list, Int32 prime, Int32 divisor)
        {
            divisor *= 10;

            foreach (var i in new[] { 1, 3, 7, 9 })
            {
                var newprime = 10 * prime + i;

                if (IsPrime(newprime))
                {
                    if (IsTruncatableFromLeft(newprime, divisor))
                    {
                        list.Add(newprime);
                    }

                    TwoSidedPrimes(list, newprime, divisor);
                }
            }
        }

        private static Boolean IsTruncatableFromLeft(Int32 n, Int32 divisor)
        {
            do
            {
                n %= divisor;
                divisor /= 10;

                if (!IsPrime(n))
                {
                    return false;
                }
            }
            while (divisor > 1);

            return true;
        }

        private static readonly Func<Int32, Boolean> IsPrime = Functional.Memoize<Int32, Boolean>(Numeric.IsPrime);
    }
}
